package org.runecore.rs2;

import java.io.FileInputStream;
import java.nio.channels.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import java.util.concurrent.*;
import java.util.logging.Logger;

import org.runecore.file.FileSystem;
import org.runecore.file.io.AbstractFileOperation;
import org.runecore.file.io.impl.SQLPlayerFileOperation;
import org.runecore.rs2.core.GameLogicCore;
import org.runecore.rs2.event.EventManager;
import org.runecore.rs2.model.World;
import org.runecore.rs2.net.NetworkHandler;
import org.runecore.rs2.net.codec.AbstractGameRevision;
import org.runecore.rs2.net.sql.SQLConnection;
import org.runecore.rs2.net.sql.SQLConnection.ReaderType;
import org.runecore.rs2.plugin.PluginFactory;
import org.runecore.util.*;

/**
 * RuneCore Aug 20, 2011
 * 
 * @author Harry Andreas<Harry@RuneCore.org>
 */
public class Environment {

	/**
	 * Logger instance
	 */
	private Logger logger = Logger.getLogger(Environment.class.getName());
	
	/**
	 * A <code>Vector</code> of usable <code>AbstractGameRevision</code>'s
	 */
	private final Vector<AbstractGameRevision> loadedRevisions = new Vector<AbstractGameRevision>();

	/**
	 * A <code>Map</code> containing objects
	 */
	protected transient Map<String, Object> environmentObjects = new HashMap<String, Object>();

	/**
	 * The <code>LogHandler</code> instance
	 */
	private final LogHandler logHandler = new LogHandler();

	/**
	 * Starts up the application environment
	 * 
	 * @throws Exception
	 */
	public void init() throws Exception {
		logger.addHandler(logHandler);
		logger.info("Starting RuneCore...");
		int processorCores = Runtime.getRuntime().availableProcessors();
		addAttribute("logHandler", new LogHandler());
		addAttribute("fileSystem", new FileSystem());
		addAttribute("logicCore", new GameLogicCore(processorCores));
		addAttribute("timedCore",
				Executors.newScheduledThreadPool(processorCores));
		addAttribute("ioCore", Executors.newFixedThreadPool(processorCores));
		addAttribute("selector", Selector.open());
		addAttribute("socketChannel", ServerSocketChannel.open());
		addAttribute("networkHandler", new NetworkHandler());
		Properties configuration = new Properties();
		configuration.load(new FileInputStream(getFS().getFileByName("config.rcf")));
		addAttribute("plugins", new PluginFactory());
		getLogger().info("Args: "+ Arrays.toString(configuration.entrySet().toArray()));
		addAttribute("name", configuration.get("name"));
		addAttribute("cycle-rate", configuration.get("cycle-rate"));
		addAttribute("ip", configuration.get("ip"));
		addAttribute("port", configuration.get("port"));
		addAttribute("rate", configuration.get("cycle-rate"));
		addAttribute("accept-cycle", configuration.get("accept-cycle"));
		addAttribute("max-players", configuration.get("max-players"));
		addAttribute("gametype",Integer.parseInt(((String) configuration.get("game-version"))));
		World.getSingleton();
		EventManager.getSingleton();
		SQLConnection sql = new SQLConnection("player.db", ReaderType.NORMAL);
		addAttribute("player-sql", sql);
		addAttribute("player-saver", new SQLPlayerFileOperation());
		((NetworkHandler) getAttribute("networkHandler")).applyNetworkConfigurations();
		scheduleTickTask();
	}

	/**
	 * Schedule's a tick task on the task thread
	 */
	public void scheduleTickTask() {
		int cycleRate = Integer.parseInt((String)getAttribute("cycle-rate"));
		getTimedExecutor().scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				try {
					((NetworkHandler) getAttribute("networkHandler")).cycle();
					EventManager.getSingleton().processQueuedEvents();
					World.getSingleton().handlePlayerProcess();
				} catch (Exception e) {
					getLogger().warning("Exception caught while processing cycle.");
					e.printStackTrace();
				}
			}
		}, cycleRate, cycleRate, TimeUnit.MILLISECONDS);
	}
	
	/**
	 * 
	 * @return
	 */
	public AbstractFileOperation getAccountManager() {
		return (AbstractFileOperation) getAttribute("player-saver");
	}
	
	/**
	 * Gets a connection for sql
	 * @param s
	 * @return
	 */
	public SQLConnection getConnection(String s) {
		return (SQLConnection) getAttribute(s + "-sql");
	}

	/**
	 * The file system
	 * @return
	 */
	public FileSystem getFS() {
		return (FileSystem) getAttribute("fileSystem");
	}

	/**
	 * Get's the <code>GameLogicCore</code> from the attributes map
	 * 
	 * @return The Executor Service
	 */
	public GameLogicCore getLogicCore() {
		return (GameLogicCore) getAttribute("logicCore");
	}

	/**
	 * Get's the IO Core
	 * 
	 * @return The <code>Executor</code> for handling File I/O operations
	 */
	public Executor getIOCore() {
		return (ExecutorService) getAttribute("ioCore");
	}

	/**
	 * Gets the timed executor service for processing
	 * 
	 * @return
	 */
	public ScheduledThreadPoolExecutor getTimedExecutor() {
		return (ScheduledThreadPoolExecutor) getAttribute("timedCore");
	}

	/**
	 * Adds an attribute
	 * 
	 * @param var
	 * @param value
	 */
	public void addAttribute(String var, Object value) {
		environmentObjects.put(var, value);
	}

	/**
	 * Get's an attribute from the Environment Attribute <code>Map</code>
	 * 
	 * @param var
	 * @return
	 */
	public Object getAttribute(String var) {
		return environmentObjects.get(var);
	}

	/**
	 * The logger instance
	 * 
	 * @return
	 */
	public Logger getLogger() {
		return logger;
	}

	/**
	 * @return the loadedRevisions
	 */
	public Vector<AbstractGameRevision> getLoadedRevisions() {
		return loadedRevisions;
	}

}